package com.go.where.family.jersey.api;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.client.JerseyWebTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.go.where.family.jersey.common.EasemobConstants;
import com.go.where.family.jersey.common.HTTPMethod;
import com.go.where.family.jersey.common.Roles;
import com.go.where.family.jersey.utils.JerseyUtils;
import com.go.where.family.jersey.vo.ClientSecretCredential;
import com.go.where.family.jersey.vo.Credential;
import com.go.where.family.jersey.vo.EndPoints;

/**
 * REST API Demo: 发送消息 REST API Jersey2.9实现
 * 
 * Doc URL: http://www.easemob.com/docs/rest/sendmessage/
 * 
 * @author
 * 
 */
public class EasemobMessages {

	private static final Logger logger = LoggerFactory
			.getLogger(EasemobMessages.class);

	private static final String APPKEY = EasemobConstants.APPKEY;
	private static final JsonNodeFactory factory = new JsonNodeFactory(false);

	// 通过app的client_id和client_secret来获取app管理员token
	private static Credential credential = new ClientSecretCredential(
			EasemobConstants.APP_CLIENT_ID, EasemobConstants.APP_CLIENT_SECRET,
			Roles.USER_ROLE_APPADMIN);

	public static void main(String[] args) {
		// 检测用户是否在线
		String targetUserName = "kenshinnuser000";
		ObjectNode usernode = getUserStatus(targetUserName);
		if (null != usernode) {
			logger.info("检测用户是否在线: " + usernode.toString());
		}

		// 给用户发一条文本消息
		String from = "kenshinnuser000";
		String targetTypeus = "users";
		ArrayNode targetusers = factory.arrayNode();
		targetusers.add("kenshinnuser001");
		targetusers.add("kenshinnuser002");
		ObjectNode txtmsg = factory.objectNode();
		txtmsg.put("msg", "Hello Easemob!");
		txtmsg.put("type", "txt");
		ObjectNode sendTxtMessageusernode = sendMessages(targetTypeus,
				targetusers, txtmsg, from);
		if (null != sendTxtMessageusernode) {
			logger.info("给用户发一条文本消息: " + sendTxtMessageusernode.toString());
		}

		// 给用户发一条透传消息
		ObjectNode cmdmsg = factory.objectNode();
		cmdmsg.put("action", "gogogo");
		cmdmsg.put("type", "cmd");
		ObjectNode sendcmdMessageusernode = sendMessages(targetTypeus,
				targetusers, cmdmsg, from);
		if (null != sendcmdMessageusernode) {
			logger.info("给用户发一条透传消息: " + sendcmdMessageusernode.toString());
		}
	}

	/**
	 * 发送透传消息给指定用户
	 * 
	 * @param fromUser
	 *            发送者 表示这个消息是谁发出来的, 可以没有这个属性, 那么就会显示是admin, 如果有的话, 则会显示是这个用户发出的
	 * @param targetUsers
	 *            接收者ID列表,列表元素为用户ID或者群组ID
	 * @param messageContent
	 *            透传的消息内容
	 * @return 发送失败时，用户id列表
	 */
	public static List<String> sendMessages(String fromUser,
			List<String> targetUsers, String messageContent) {
		// users 给用户发消息, chatgroups 给群发消息
		String targetTypeus = "users";
		// 注意这里需要用数组,数组长度建议不大于20
		ArrayNode targets = factory.arrayNode();
		if (targetUsers != null && !targetUsers.isEmpty()) {
			for (String target : targetUsers) {
				targets.add(target);
			}
		}
		// 给用户发送时数组元素是用户名,给群组发送时数组元素是groupid
		List<String> failedCustomerId = new ArrayList<String>();
		if (targetUsers != null && targetUsers.size() <= 20) {
			failedCustomerId = sendSingleMessage(fromUser, targets,
					messageContent, targetTypeus);
		} else if (targetUsers != null && targetUsers.size() > 20) {
			int targetSize = targetUsers.size();
			ArrayNode tmpArrayNode = factory.arrayNode();
			for (int i = 0; i < targetSize; i++) {
				tmpArrayNode.add(targetUsers.get(i));
				// 20 records on one migration
				if ((i + 1) % 20 == 0) {
					List<String> failedCId = sendSingleMessage(fromUser,
							tmpArrayNode, messageContent, targetTypeus);
					failedCustomerId.addAll(failedCId);
					tmpArrayNode.removeAll();
					continue;
				}
				if (i > (targetSize / 20 * 20 - 1)) {
					List<String> failedCId = sendSingleMessage(fromUser,
							tmpArrayNode, messageContent, targetTypeus);
					failedCustomerId.addAll(failedCId);
					tmpArrayNode.removeAll();
				}
			}

		}

		return failedCustomerId;
	}

	private static List<String> sendSingleMessage(String fromUser,
			ArrayNode targets, String messageContent, String targetTypeus) {
		// 给用户发一条透传消息
		ObjectNode cmdmsg = factory.objectNode();
		cmdmsg.put("action", messageContent);
		// 消息类型
		cmdmsg.put("type", "cmd");
		ObjectNode sendcmdMessageusernode = sendMessages(targetTypeus, targets,
				cmdmsg, fromUser);
		List<String> failedCustomerId = new ArrayList<String>();
		if (null != sendcmdMessageusernode
				&& sendcmdMessageusernode.get("message") == null) {
			logger.info("给用户发一条透传消息及结果返回: " + messageContent + ","
					+ sendcmdMessageusernode.toString());
			for (int i = 0; i < targets.size(); i++) {
				String resultStr = sendcmdMessageusernode.path(
						targets.path(i).asText()).asText();
				if (!"success".equals(resultStr)) {
					failedCustomerId.add(targets.path(i).asText());
				}
			}
		}
		return failedCustomerId;
	}

	/**
	 * 检测用户是否在线
	 * 
	 * @param targetUserName
	 * @return
	 */
	public static ObjectNode getUserStatus(String targetUserName) {
		ObjectNode objectNode = factory.objectNode();

		// check appKey format
		if (!JerseyUtils.match("^(?!-)[0-9a-zA-Z\\-]+#[0-9a-zA-Z]+", APPKEY)) {
			logger.error("Bad format of Appkey: " + APPKEY);
			objectNode.put("message", "Bad format of Appkey");
			return objectNode;
		}

		// check properties that must be provided
		if (StringUtils.isEmpty(targetUserName)) {
			logger.error("You must provided a targetUserName .");
			objectNode.put("message", "You must provided a targetUserName.");
			return objectNode;
		}

		try {
			JerseyWebTarget webTarget = EndPoints.USERS_TARGET.resolveTemplate(
					"org_name", APPKEY.split("#")[0]).resolveTemplate(
					"app_name", APPKEY.split("#")[1]).path(targetUserName)
					.path("status");

			objectNode = JerseyUtils.sendRequest(webTarget, null, credential,
					HTTPMethod.METHOD_GET, null);
			String userStatus = objectNode.get("data").path(targetUserName)
					.asText();
			if ("online".equals(userStatus)) {
				logger.info(String.format("The status of user[%s] is : [%s] .",
						targetUserName, userStatus));
			} else if ("offline".equals(userStatus)) {
				logger.info(String.format("The status of user[%s] is : [%s] .",
						targetUserName, userStatus));
			}
		} catch (Exception e) {
			logger.error("检测用户是否在线出现异常", e);
			objectNode.put("message", "检测用户是否在线出现异常");
			return objectNode;
		}

		return objectNode;
	}

	/**
	 * 发送消息
	 * 
	 * @param targetType
	 *            消息投递者类型：users 用户, chatgroups 群组
	 * @param target
	 *            接收者ID 必须是数组,数组元素为用户ID或者群组ID
	 * @param msg
	 *            消息内容
	 * @param from
	 *            发送者 表示这个消息是谁发出来的, 可以没有这个属性, 那么就会显示是admin, 如果有的话, 则会显示是这个用户发出的
	 * @return 请求响应
	 */
	public static ObjectNode sendMessages(String targetType, ArrayNode target,
			ObjectNode msg, String from) {
		ObjectNode objectNode = factory.objectNode();
		ObjectNode dataNode = factory.objectNode();
		// check appKey format
		if (!JerseyUtils.match("^(?!-)[0-9a-zA-Z\\-]+#[0-9a-zA-Z]+", APPKEY)) {
			logger.error("Bad format of Appkey: " + APPKEY);
			objectNode.put("message", "Bad format of Appkey");
			return objectNode;
		}

		// check properties that must be provided
		if (!("users".equals(targetType) || "chatgroups".equals(targetType))) {
			logger.error("TargetType must be users or chatgroups .");
			objectNode.put("message",
					"TargetType must be users or chatgroups .");
			return objectNode;
		}

		try {
			// 构造消息体
			dataNode.put("target_type", targetType);
			dataNode.put("target", target);
			dataNode.put("msg", msg);
			dataNode.put("from", from);

			JerseyWebTarget webTarget = EndPoints.MESSAGES_TARGET
					.resolveTemplate("org_name", APPKEY.split("#")[0])
					.resolveTemplate("app_name", APPKEY.split("#")[1]);

			objectNode = JerseyUtils.sendRequest(webTarget, dataNode,
					credential, HTTPMethod.METHOD_POST, null);

			objectNode = (ObjectNode) objectNode.get("data");
			for (int i = 0; i < target.size(); i++) {
				String resultStr = objectNode.path(target.path(i).asText())
						.asText();
				if ("success".equals(resultStr)) {
					logger.info(String.format(
							"Message has been send to user[%s] successfully .",
							target.path(i).asText()));
				} else if (!"success".equals(resultStr)) {
					logger.info(String.format(
							"Message has been send to user[%s] failed .",
							target.path(i).asText()));
				}
			}

		} catch (Exception e) {
			logger.error("发送消息异常", e);
			objectNode.put("message", "发送消息异常");

		}

		return objectNode;
	}
}
